/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "http_server.h"
#include "node/sys/console.h"
#include "node/net/http/http_request.h"
#include "node/net/http/http_response.h"
#include <cassert>
using Console = ::node::sys::Console;
using HttpRequest = ::node::http::HttpRequest;
using HttpResponse = ::node::http::HttpResponse;

namespace node {
    namespace http {

        HttpServer::HttpServer(const char *doc_root)
            : doc_root_(doc_root)
            , ebase_(NULL)
            , edummy_(NULL)
            , http_(NULL)
            , bound_handle_(NULL)
            , user_data_(NULL)
        {
            assert(doc_root);
        }

        HttpServer::~HttpServer(void)
        {
            if (http_) {
                evhttp_free(http_);
            }
            if (edummy_) {
                event_free(edummy_);
            }
            if (ebase_) {
                event_base_free(ebase_);
            }
        }

        bool HttpServer::Start(const char *host, int port)
        {
            do 
            {
                ebase_ = event_base_new();
                if (!ebase_) {
                    Console::Error("[HttpServer::Start] Couldn't create an event_base\n");
                    break;
                }

                edummy_ = event_new(ebase_, -1, EV_TIMEOUT | EV_PERSIST, 
                    &HttpServer::OnTimeout, this);
                if (!edummy_) {
                    Console::Error("[HttpServer::Start] Couldn't create an event\n");
                    break;
                }
                struct timeval tv = { 1, 0 };
                if (0 != event_add(edummy_, &tv)) {
                    Console::Error("[HttpServer::Start] Add dummy event failed\n");
                    break;
                }

                http_ = evhttp_new(ebase_);
                if (!http_) {
                    Console::Error("[HttpServer::Start] Couldn't create evhttp\n");
                    break;
                }

                evhttp_set_gencb(http_, &HttpServer::OnRequest, this);

                bound_handle_ = evhttp_bind_socket_with_handle(http_, host, port);
                if (!bound_handle_) {
                    Console::Error("[HttpServer::Start]  Couldn't bind to port %d\n", port);
                    break;
                }

                return true;
            } while (false);

            if (http_) {
                evhttp_free(http_);
                http_ = NULL;
            }
            if (edummy_) {
                event_free(edummy_);
                edummy_ = NULL;
            }
            if (ebase_) {
                event_base_free(ebase_);
                ebase_ = NULL;
            }

            return false;
        }

        void HttpServer::Stop(void)
        {
            assert(ebase_ != NULL);
            event_base_loopbreak(ebase_);
        }

        void HttpServer::Run(void)
        {
            assert(ebase_ != NULL);
            event_base_dispatch(ebase_);
        }

        void HttpServer::OnRequest(struct evhttp_request *request, void *arg)
        {
            assert(request && arg);
            HttpServer *http_serv = static_cast<HttpServer*>(arg);
            assert(http_serv->request_callback_);

            HttpRequest *req = new HttpRequest(request);
            if (req && req->IsParseOk()) {
                HttpResponse *resp = new HttpResponse(req);
                if (resp && resp->IsInitOk()) {
                    http_serv->request_callback_(req, resp, http_serv->user_data_);
                    delete resp;
                    delete req;
                    return;
                }

                if (resp) {
                    delete resp;
                }
            }

            if (req) {
                delete req;
            }
        }

        void HttpServer::OnTimeout(evutil_socket_t, short, void *)
        {
            // FOR STOP USE
        }
    }
}